#pragma once
#include <complex>

using namespace System;
using namespace NUnit::Framework;
using namespace LatoolNet;

namespace LatoolNetTest {

	[TestFixture]
	public ref class ComplexTest {
	private:
	public:
#pragma region operator +
		[Test]
		void TestAddComplexComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double real2 = randomgen->NextDouble() * 10 - 5;
			double imag2 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c1(real1, imag1);
			Complex c2(real2, imag2);

			std::complex<double> sc1(real1, imag1);
			std::complex<double> sc2(real2, imag2);

			Complex actual = c1 + c2;
			std::complex<double> expected = sc1 + sc2;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex + Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex + Complex");	

		};


		[Test]
		void TestAddDoubleComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = d + c;
			std::complex<double> expected = d + sc;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Double + Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Double + Complex");	

		};

		[Test]
		void TestAddComplexDouble() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = c + d;
			std::complex<double> expected = sc + d;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex + double");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex + double");	

		};

#pragma endregion
#pragma region operator -
		[Test]
		void TestSubComplexComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double real2 = randomgen->NextDouble() * 10 - 5;
			double imag2 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c1(real1, imag1);
			Complex c2(real2, imag2);

			std::complex<double> sc1(real1, imag1);
			std::complex<double> sc2(real2, imag2);

			Complex actual = c1 - c2;
			std::complex<double> expected = sc1 - sc2;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex - Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex - Complex");	

		};

		[Test]
		void TestSubDoubleComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = d - c;
			std::complex<double> expected = d - sc;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Double - Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Double - Complex");	

		};

		[Test]
		void TestSubComplexDouble() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = c - d;
			std::complex<double> expected = sc - d;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex - double");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex - double");	

		};
#pragma endregion
		
#pragma region operator *
		[Test]
		void TestMultiplyComplexComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double real2 = randomgen->NextDouble() * 10 - 5;
			double imag2 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c1(real1, imag1);
			Complex c2(real2, imag2);

			std::complex<double> sc1(real1, imag1);
			std::complex<double> sc2(real2, imag2);

			Complex actual = c1 * c2;
			std::complex<double> expected = sc1 * sc2;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex * Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex * Complex");	

		};


		[Test]
		void TestMultiplyDoubleComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = d * c;
			std::complex<double> expected = d * sc;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Double * Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Double * Complex");	

		};

		[Test]
		void TestMultiplyComplexDouble() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = c * d;
			std::complex<double> expected = sc * d;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex * double");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex * double");	

		};

#pragma endregion
#pragma region operator /
		[Test]
		void TestDivComplexComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double real2 = randomgen->NextDouble() * 10 - 5;
			double imag2 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c1(real1, imag1);
			Complex c2(real2, imag2);

			std::complex<double> sc1(real1, imag1);
			std::complex<double> sc2(real2, imag2);

			Complex actual = c1 / c2;
			std::complex<double> expected = sc1 / sc2;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex * Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex * Complex");	

		};


		[Test]
		void TestDivDoubleComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = d / c;
			std::complex<double> expected = d / sc;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Double * Complex");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Double * Complex");	

		};

		[Test]
		void TestDivComplexDouble() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = c / d;
			std::complex<double> expected = sc / d;

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Complex * double");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Complex * double");	

		};

#pragma endregion
#pragma region Pow
		[Test]
		void TestPowComplexComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double real2 = randomgen->NextDouble() * 10 - 5;
			double imag2 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c1(real1, imag1);
			Complex c2(real2, imag2);

			std::complex<double> sc1(real1, imag1);
			std::complex<double> sc2(real2, imag2);

			Complex actual = Complex::Pow(c1, c2);
			std::complex<double> expected = std::pow(sc1, sc2);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Pow(Complex, Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Pow(Complex, Complex)");	


		};

		[Test]
		void TestPowDoubleComplex() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Pow(d, c);
			std::complex<double> expected = std::pow(d, sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Pow(double, Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Pow(double, Complex)");	

		};

		[Test]
		void TestPowComplexDouble() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;
			double d = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Pow(c, d);
			std::complex<double> expected = std::pow(sc, d);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Pow(Complex, double)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Pow(Complex, double)");	

		};
#pragma endregion
		

		[Test]
		void TestConj(){

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Conj(c);
			std::complex<double> expected = std::conj(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Conj(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Conj(Complex)");	

		};

		[Test]
		void TestNorm() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			double actual = Complex::Norm(c);
			double expected = std::norm(sc);

			Assert::AreEqual(expected, actual, 1e-10, "Test: Norm(Complex)");	

		};

		[Test]
		void TestSqrt() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Sqrt(c);
			std::complex<double> expected = std::sqrt(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Sqrt(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Sqrt(Complex)");	

		};

		[Test]
		void TestExp() {
			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Exp(c);
			std::complex<double> expected = std::exp(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Exp(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Exp(Complex)");	


		};

		[Test]
		void TestLog() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Log(c);
			std::complex<double> expected = std::log(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Log(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Log(Complex)");	

		};

		[Test]
		void TestLog10() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Log10(c);
			std::complex<double> expected = std::log10(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Log10(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Log10(Complex)");	

		};

		[Test]
		void TestSin() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Sin(c);
			std::complex<double> expected = std::sin(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Sin(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Sin(Complex)");	

		};

		[Test]
		void TestSinh() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Sinh(c);
			std::complex<double> expected = std::sinh(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Sinh(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Sinh(Complex)");	

		};

		[Test]
		void TestCos() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Cos(c);
			std::complex<double> expected = std::cos(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Cos(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Cos(Complex)");	

		};

		[Test]
		void TestCosh() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Cosh(c);
			std::complex<double> expected = std::cosh(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Cosh(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Cosh(Complex)");	

		};

		[Test]
		void TestTan() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Tan(c);
			std::complex<double> expected = std::tan(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Tan(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Tan(Complex)");	

		};

		[Test]
		void TestTanh() {

			Random ^ randomgen = gcnew Random();

			double real1 = randomgen->NextDouble() * 10 - 5;
			double imag1 = randomgen->NextDouble() * 10 - 5;

			Complex c(real1, imag1);
			std::complex<double> sc(real1, imag1);

			Complex actual = Complex::Tanh(c);
			std::complex<double> expected = std::tanh(sc);

			Assert::AreEqual(expected.real(), actual.Real, 1e-10, "Test: Tanh(Complex)");	
			Assert::AreEqual(expected.imag(), actual.Imag, 1e-10, "Test: Tanh(Complex)");	

		};





	};
}